package api

import (
	"fmt"
	"github.com/chicken-team-outside/chicken_transmission/proxy_server/middleware"
	"github.com/chicken-team-outside/chicken_transmission/proxy_server/model"
	"github.com/chicken-team-outside/chicken_transmission/proxy_server/utils"
	"github.com/gin-gonic/gin"
	"go.uber.org/zap"
)

func initV1User(group *gin.RouterGroup) {
	group.POST("/register", registerUser)
	group.GET("/check_email", checkEmailExist)
	group.GET("/check_user", checkUserExist)
	group.POST("/reset_password", resetPassword)
	group.POST("/login", login)
	group.GET("/logout", middleware.AuthWare, logout)
	group.GET("/user_info", middleware.AuthWare, getUserInfo)
}

type RegisterUserQuery struct {
	User         string `json:"user"`
	Email        string `json:"email"`
	Phone        string `json:"phone"`
	Password     string `json:"password"`
	EmailCaptcha string `json:"email_captcha"`
}

func checkUserExist(ctx *gin.Context) {
	user := ctx.Query("user")
	if user == "" {
		utils.BadRequest(ctx, "user is required")
		return
	}
	if model.ExistsUser(user) {
		utils.Success(ctx, "user exists", true)
	} else {
		utils.Success(ctx, "user not exists", false)
	}
}

func checkEmailExist(ctx *gin.Context) {
	email := ctx.Query("email")
	if email == "" {
		utils.BadRequest(ctx, "email is required!")
		return
	}
	if model.ExistsEmail(email) {
		utils.Success(ctx, "email exists", true)
	} else {
		utils.Success(ctx, "email not exists", false)
	}
}

func registerUser(ctx *gin.Context) {
	query := &RegisterUserQuery{}
	err := ctx.ShouldBindJSON(query)
	if err != nil {
		zap.L().Info("failed to bind json", zap.Error(err))
		utils.BadRequest(ctx, "bad parameters!")
		return
	}
	if query.Email == "" || query.User == "" || query.Password == "" || query.EmailCaptcha == "" {
		utils.BadRequest(ctx, "user, email, password and email_captcha are required!")
		return
	}
	if !verifyRegisterEmailCaptcha(query.Email, query.EmailCaptcha) {
		utils.BadRequest(ctx, "wrong email captcha!")
		return
	}
	if model.ExistsUser(query.User) {
		utils.BadRequest(ctx, "user name already registered!")
		return
	}
	if model.ExistsEmail(query.Email) {
		utils.BadRequest(ctx, "email already registered!")
		return
	}
	user := model.User{
		User:   query.User,
		Passwd: query.Password,
		Phone:  query.Phone,
		Email:  query.Email,
	}
	err = model.AddUser(&user)
	if err != nil {
		zap.L().Error("failed to add user", zap.Error(err), zap.Any("user", user), utils.UnknownError(ctx))
		return
	}
	token, err := middleware.SetLoginToken(ctx, user.Id)
	if err != nil {
		zap.L().Error("set login token failed", zap.Error(err), utils.UnknownError(ctx))
		return
	}
	removeRegisterEmailCaptcha(query.Email)
	utils.Success(ctx, "register success", gin.H{
		"token": token,
	})
}

type ResetPasswordQuery struct {
	Email        string `json:"email"`
	NewPassword  string `json:"new_password"`
	EmailCaptcha string `json:"email_captcha"`
}

func resetPassword(ctx *gin.Context) {
	query := &ResetPasswordQuery{}
	err := ctx.ShouldBindJSON(query)
	if err != nil {
		zap.L().Info("failed to bind json", zap.Error(err))
		utils.BadRequest(ctx, "bad parameters!")
		return
	}
	if query.Email == "" || query.NewPassword == "" || query.EmailCaptcha == "" {
		utils.BadRequest(ctx, "email, new_password and email_captcha are required!")
		return
	}
	if !verifyResetPasswordEmailCaptcha(query.Email, query.EmailCaptcha) {
		utils.BadRequest(ctx, "wrong email captcha!")
		return
	}
	err = model.UpdateUserPassword(query.Email, query.NewPassword)
	if err != nil {
		zap.L().Error("failed to update user password", zap.Error(err), utils.UnknownError(ctx))
		return
	}
	user, err := model.GetUserInfoByEmail(query.Email)
	if err != nil {
		zap.L().Error("can't get user info", zap.Error(err), utils.UnknownError(ctx))
		return
	}
	token, err := middleware.SetLoginToken(ctx, user.Id)
	if err != nil {
		zap.L().Error("can't set login token to cache!", zap.Error(err), utils.UnknownError(ctx))
		return
	}
	removeResetPasswordEmailCaptcha(query.Email)
	utils.Success(ctx, "reset password success!", gin.H{
		"token": token,
	})
}

type LoginQuery struct {
	User     string `json:"user"`
	Password string `json:"password"`
	Cid      string `json:"cid"`
	Captcha  string `json:"captcha"`
}

func login(ctx *gin.Context) {
	query := &LoginQuery{}
	err := ctx.BindJSON(query)
	if err != nil {
		zap.L().Info("failed to bind json", zap.Error(err))
		utils.BadRequest(ctx, "bad parameters!")
		return
	}
	if query.User == "" || query.Password == "" || query.Captcha == "" || query.Cid == "" {
		utils.BadRequest(ctx, "user, password, captcha and cid are required!")
		return
	}
	if !verifyImgCaptcha(query.Cid, query.Captcha) {
		utils.BadRequest(ctx, "wrong captcha!")
		return
	}
	user, err := model.GetUserInfoByEmail(query.User)
	if err != nil {
		zap.L().Error("can't get user info", zap.Error(err), zap.Any("user", query.User), utils.UnknownError(ctx))
		return
	}
	if user == nil {
		utils.BadRequest(ctx, "user not exists!")
		return
	}
	if user.Status != model.ActiveStatue {
		utils.BadRequest(ctx, fmt.Sprintf("user is %s, please contact admin!", user.Status))
		return
	}
	if !user.CheckPassword(query.Password) {
		utils.BadRequest(ctx, "wrong email or password!")
		return
	}
	token, err := middleware.SetLoginToken(ctx, user.Id)
	if err != nil {
		zap.L().Error("set login token failed", zap.Error(err), utils.UnknownError(ctx))
		return
	}
	utils.Success(ctx, "success!", gin.H{
		"token": token,
	})
}

func logout(ctx *gin.Context) {
	userId, ok := middleware.GetUserId(ctx)
	if !ok {
		utils.BadRequest(ctx, "token is invalid!")
		return
	}
	middleware.ClearTokenByUser(ctx, userId)
	middleware.ClearToken(ctx)
	utils.Success(ctx, "logout success!", nil)
	return
}

func getUserInfo(ctx *gin.Context) {
	userId, exists := middleware.GetUserId(ctx)
	if exists {
		user, err := model.GetUserInfoById(userId)
		if err != nil {
			zap.L().Warn("Could not get user", zap.Error(err), utils.UnknownError(ctx))
		} else {
			if user != nil {
				utils.Success(ctx, "success!", gin.H{
					"id":    user.Id,
					"user":  user.User,
					"email": user.Email,
					"phone": user.Phone,
				})
			} else {
				utils.BadRequest(ctx, "user not exist!")
			}
		}
	} else {
		zap.L().Warn("not found user info from middleware", utils.UnknownError(ctx))
	}
}
